﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;

namespace MedicWave.Visualization3d
{


    /// <summary>
    /// Struct that contains the M/Z and Intensity - used in sorting and filtering
    /// </summary>
    struct DataTriplet
    {
        /// <summary>
        /// Retention time
        /// </summary>
        public float m_fRT;
        /// <summary>
        /// M/Z
        /// </summary>
        public float m_fMZ;
        /// <summary>
        /// Intensity
        /// </summary>
        public float m_fIntensity;

    }



    /// <summary>
    /// An object that contains a sorted array of samples with the same Retention Time - a spectrum
    /// </summary>
    class Spectrum
    {
        public float m_RT;
        public DataPoint[] m_PD;

        public int NumberOfSamples
        {
            get
            {
                return m_PD.Length;
            }
        }

        public Spectrum(DataPoint[] data, float fRetentionTime)
        {
            m_RT = fRetentionTime;
            m_PD = data;

        }
        public Spectrum()
        {
            m_RT = 0;
            m_PD = null;

        }
    }

    /// <summary>
    /// A struct that defines a plot subset (data range+filtering)
    /// </summary>
    struct PlotSubsetDesc
    {
        /// <summary>
        /// If true, then data is filtered globally, not per spectrum
        /// </summary>
        public bool m_bGlobalIntensity;

        /// <summary>
        /// A value describing how many data samples to keep.
        /// </summary>
        /// <example>A value of 0.1 means 10% of data samples with highest intensity. </example>
        public float m_fIntensityTreshold;

        public float m_fMinRetentionTime;
        public float m_fMaxRetentionTime;
        public float m_fMinMZ;
        public float m_fMaxMZ;
        public int m_iResolutionRetentionTime;
        public int m_iResolutionMZ;
    }

    /// <summary>
    /// A helper struct to describe current plot zoom
    /// </summary>
    struct PlotZoom
    {
        public float m_fRTSubset;//=1
        public float m_fRTOffset;//=0
        public float m_fMZSubset;//=1
        public float m_fMZOffset;//=0

        public void Reset()
        {
            m_fRTOffset = m_fMZOffset = 0;
            m_fMZSubset = m_fRTSubset = 1;
        }
    }

    /// <summary>
    /// Plot view settings.
    /// </summary>
    /// <remarks>Changing view settings doesn't involve any time consuming operations.</remarks>
    struct ViewSettings
    {

        bool m_bLockPlotType;

        public bool LockPlotType
        {
            get { return m_bLockPlotType; }
            set { m_bLockPlotType = value; }
        }

        bool m_printerColors;

        public bool PrinterColors
        {
            get { return m_printerColors; }
            set { m_printerColors = value; }
        }

        Color m_bkgColor;

        public Color BkgColor
        {
            get { return m_bkgColor; }
            set { m_bkgColor = value; }
        }

        bool m_UseSpecular;

        public bool UseSpecular
        {
            get { return m_UseSpecular; }
            set { m_UseSpecular = value; }
        }


        PlotZoom m_PlotSubset;

        public PlotZoom PlotSubset
        {
            get { return m_PlotSubset; }
            set { m_PlotSubset = value; }
        }

        bool m_ShowGrid;

        public void SetShowGrid(bool value)
        {
            m_ShowGrid = value;
        }
        public bool ShowGrid
        {
            get { return m_ShowGrid; }
            set { m_ShowGrid = value; }
        }

        Plot3DControl.AxisTypes m_AxisType;

        ProjectionType m_eProjectionType;

        public ProjectionType ProjectionType
        {
            get { return m_eProjectionType; }
            set { m_eProjectionType = value; }
        }

        string m_PlotTitle;

        public string PlotTitle
        {
            get { return m_PlotTitle; }
            set { m_PlotTitle = value; }
        }

        string m_MZAxisLabel;

        public string MZAxisLabel
        {
            get { return m_MZAxisLabel; }
            set { m_MZAxisLabel = value; }
        }
        string m_RTAxisLabel;

        public string RTAxisLabel
        {
            get { return m_RTAxisLabel; }
            set { m_RTAxisLabel = value; }
        }
        string m_IntensityAxisLabel;

        public string IntensityAxisLabel
        {
            get { return m_IntensityAxisLabel; }
            set { m_IntensityAxisLabel = value; }
        }
        public Plot3DControl.AxisTypes AxisType
        {
            get { return m_AxisType; }
            set { m_AxisType = value; }
        }

        public void Reset()
        {
            m_PlotSubset.m_fMZOffset = 0;
            m_PlotSubset.m_fRTOffset = 0;
            m_PlotSubset.m_fMZSubset = 1;
            m_PlotSubset.m_fRTSubset = 1;

            m_bkgColor = Color.FromArgb(255, 40, 40, 40);
            m_UseSpecular = false;
            m_printerColors = false;
            m_ShowGrid = true;
            m_bLockPlotType = false;
        }
    }



    /// <summary>
    /// A class containing all plot settings
    /// </summary>
    class InternalPlotSettings : ICloneable
    {

        private bool m_bOverlapLines;

        public bool OverlapLines
        {
            get { return m_bOverlapLines; }
            set { m_bOverlapLines = value; }
        }

        const int m_ResolutionDefaultMZ = 500;
        PlotSubsetDesc m_PlotSubsetDescription;

        public PlotSubsetDesc PlotSubsetDescription
        {
            get { return m_PlotSubsetDescription; }
            set { m_PlotSubsetDescription = value; }
        }

        PlotType m_ePlotType;

        public PlotType PlotType
        {
            get { return m_ePlotType; }
            set { m_ePlotType = value; }
        }

        GradientControl m_cColorGradient;
        public GradientControl ColorGradient
        {
            get { return m_cColorGradient; }
            set { m_cColorGradient = value; }
        }



        ViewSettings m_ViewSettings;

        public ViewSettings ViewSettings
        {
            get { return m_ViewSettings; }
            set { m_ViewSettings = value; }
        }


        bool m_bLogarithmicIntensity;

        public bool LogarithmicIntensity
        {
            get { return m_bLogarithmicIntensity; }
            set { m_bLogarithmicIntensity = value; }
        }


        bool m_bDisableResampling;

        public bool DisableResampling
        {
            get { return m_bDisableResampling; }
            set { m_bDisableResampling = value; }
        }

        


        public InternalPlotSettings()
        {
            m_cColorGradient = new GradientControl();
            Reset();

        }
        public InternalPlotSettings(InternalPlotData plotData)
        {
            m_cColorGradient = new GradientControl();
            m_bOverlapLines = false;
            
            Reset();
            m_PlotSubsetDescription.m_bGlobalIntensity = false;
            m_PlotSubsetDescription.m_fIntensityTreshold = 5;
            m_PlotSubsetDescription.m_fMaxMZ = plotData.MaxMZ;
            m_PlotSubsetDescription.m_fMinMZ = plotData.MinMZ;
            m_PlotSubsetDescription.m_fMaxRetentionTime = plotData.MaxRetentionTime;
            m_PlotSubsetDescription.m_fMinRetentionTime = plotData.MinRetentionTime;
            m_PlotSubsetDescription.m_iResolutionMZ = m_ResolutionDefaultMZ;
            m_PlotSubsetDescription.m_iResolutionRetentionTime = plotData.PlotSamples.Length;

        }

        public void Reset()
        {
            m_ViewSettings.Reset();
            m_cColorGradient = new GradientControl();
            m_cColorGradient.AddColor(Color.DarkGreen);
            m_cColorGradient.AddColor(Color.Yellow);
            m_cColorGradient.AddColor(Color.Orange);
            m_cColorGradient.AddColor(Color.Red);
            m_ePlotType = PlotType.Surface;
            m_bDisableResampling = false;
            m_PlotSubsetDescription.m_fIntensityTreshold = 5;

            m_PlotSubsetDescription.m_fMaxMZ = 0;
            m_PlotSubsetDescription.m_fMinMZ = 0;
            m_PlotSubsetDescription.m_fMaxRetentionTime = 0;
            m_PlotSubsetDescription.m_fMinRetentionTime = 0;

        }

        public void Update(PlotSettings newSettings)
        {
            m_PlotSubsetDescription.m_fMaxMZ = newSettings.MaxMZ;
            m_PlotSubsetDescription.m_fMinMZ = newSettings.MinMZ;
            m_PlotSubsetDescription.m_fMaxRetentionTime = newSettings.MaxRT;
            m_PlotSubsetDescription.m_fMinRetentionTime = newSettings.MinRT;

            if (newSettings.FilteringType != DataFilteringType.NoFiltering)
            {
                m_PlotSubsetDescription.m_fIntensityTreshold = newSettings.FilteringAmount;
                m_PlotSubsetDescription.m_bGlobalIntensity = newSettings.FilteringType == DataFilteringType.Plot ? true : false;
            }
            else
            {
                m_PlotSubsetDescription.m_fIntensityTreshold = 100;
            }


            m_bLogarithmicIntensity = newSettings.LogarithmicIntensity;

            m_ePlotType = newSettings.PlotType;

            m_ViewSettings.ProjectionType = newSettings.PlotProjectionType;

            m_ViewSettings.ShowGrid = newSettings.ShowGrid;

            m_ViewSettings.LockPlotType = newSettings.LockPlotType;

            m_bDisableResampling = newSettings.DisableResampling;

        }




        #region ICloneable Members

        public object Clone()
        {
            InternalPlotSettings clonedSettings = (InternalPlotSettings)MemberwiseClone();
            clonedSettings.m_cColorGradient = (GradientControl)m_cColorGradient.Clone();
            return clonedSettings;
        }

        #endregion
    }

    /// <summary>
    /// A class containing plot data
    /// </summary>
    class InternalPlotData
    {

        /// <summary>
        /// Converts data to internal representation, so changes here don't affect external api
        /// </summary>
        /// <param name="rawData">Plot data</param>
        /// <returns>Internal representation of plot data</returns>
        static public InternalPlotData FromRawData(PlotData rawData)
        {            
            Spectrum[] scanArray = new Spectrum[rawData.PlotScans.Length];
            List<DataPoint> currentRow = new List<DataPoint>();
            for (int rtIndex = 0; rtIndex < scanArray.Length; rtIndex++)
            {
                Spectrum vec = new Spectrum();                
                currentRow.Clear();
                for (int scans = 0; scans < rawData.PlotScans[rtIndex].ScanData.Length; scans++ )
                {
                    currentRow.Add(rawData.PlotScans[rtIndex].ScanData[scans]);

                    #if DEBUG
                    if (scans > 0 && currentRow[scans].MZ < currentRow[scans - 1].MZ)
                    {
                        //throw new Exception("MZ is not sorted!!");                        
                    }
                    #endif
                }
                currentRow.Sort(CompareDataSampleByMz);
                //vec.m_PD = rawData.PlotScans[rtIndex].ScanData;
                vec.m_PD = currentRow.ToArray();
                //vec.m_PD = new DataPoint[rawData.PlotScans[rtIndex].ScanData.Length];
                //for(int mzIndex=0;mzIndex<vec.m_PD.Length;mzIndex++)
                //{
                //    vec.m_PD[mzIndex] = new DataPoint();
                //    vec.m_PD[mzIndex].Intensity=rawData.PlotScans[rtIndex].ScanData[mzIndex].Intensity;
                //    vec.m_PD[mzIndex].MZ=rawData.PlotScans[rtIndex].ScanData[mzIndex].MZ;
                //}

                vec.m_RT = rawData.PlotScans[rtIndex].ScanRT;
                scanArray[rtIndex] = vec;
            }
            InternalPlotData newData = new InternalPlotData(scanArray);
            ViewSettings viewSettings = newData.PlotSettings.ViewSettings;
            viewSettings.MZAxisLabel = rawData.MzAxisText;
            viewSettings.RTAxisLabel = rawData.RtAxisText;
            viewSettings.IntensityAxisLabel = rawData.IntensityAxisText;

            viewSettings.PlotTitle = rawData.PlotName;
            newData.m_PlotSettings.ViewSettings = viewSettings;

            return newData;
        }


        //plot range
        private float m_fMinRetentionTime;

        public float MinRetentionTime
        {
            get { return m_fMinRetentionTime; }
        }
        private float m_fMaxRetentionTime;

        public float MaxRetentionTime
        {
            get { return m_fMaxRetentionTime; }
        }
        private float m_fMinIntensity;

        public float MinIntensity
        {
            get { return m_fMinIntensity; }
        }
        private float m_fMaxIntensity;

        public float MaxIntensity
        {
            get { return m_fMaxIntensity; }
        }
        private float m_fMinMZ;

        public float MinMZ
        {
            get { return m_fMinMZ; }
        }
        private float m_fMaxMZ;

        public float MaxMZ
        {
            get { return m_fMaxMZ; }
        }

        public float MZRange
        {
            get
            {
                return MaxMZ - MinMZ;
            }
        }
        public float RTRange
        {
            get
            {
                return MaxRetentionTime - MinRetentionTime;
            }
        }
        private InternalPlotSettings m_PlotSettings;

        public InternalPlotSettings PlotSettings
        {
            get { return m_PlotSettings; }
            set { m_PlotSettings = value; }
        }

        private Spectrum[] m_PlotSamples;

        public Spectrum[] PlotSamples
        {
            get { return m_PlotSamples; }
            set
            {
                m_PlotSamples = value;
            }
        }

        public int RetentionScansCount
        {
            get
            {
                return m_PlotSamples.Length;
            }
        }

        public InternalPlotData(Spectrum[] plotData)
        {
            SetData(plotData);
            m_PlotSettings = new InternalPlotSettings(this);
        }

        public void SetData(Spectrum[] plotData)
        {
            m_fMinIntensity = 0;
            m_fMaxIntensity = 0;
            m_fMinRetentionTime = 0;
            m_fMaxRetentionTime = 0;
            m_fMinMZ = 0;
            m_fMaxMZ = 0;
            m_PlotSamples = plotData;

            m_fMinIntensity = m_fMaxIntensity = 0;
            m_fMinMZ = m_fMaxMZ = 0;
            m_fMinRetentionTime = m_fMaxRetentionTime = 0;

            if (plotData.Length == 0) return;
            if (plotData[0].m_PD.Length == 0) return;

            m_fMinIntensity = m_fMaxIntensity = plotData[0].m_PD[0].Intensity;
            m_fMinMZ = m_fMaxMZ = plotData[0].m_PD[0].MZ;
            m_fMinRetentionTime = m_fMaxRetentionTime = plotData[0].m_RT;

            //compute plot bounds

            for (int rtRow = 0; rtRow < plotData.Length; rtRow++)
            {                
                if (plotData[rtRow].m_RT > m_fMaxRetentionTime)
                    m_fMaxRetentionTime = plotData[rtRow].m_RT;
                else if (plotData[rtRow].m_RT < m_fMinRetentionTime)
                    m_fMinRetentionTime = plotData[rtRow].m_RT;

                for (int mzSample = 0; mzSample < plotData[rtRow].m_PD.Length; mzSample++)
                {
                    if (plotData[rtRow].m_PD[mzSample].Intensity > m_fMaxIntensity)
                        m_fMaxIntensity = plotData[rtRow].m_PD[mzSample].Intensity;
                    else if (plotData[rtRow].m_PD[mzSample].Intensity < m_fMinIntensity)
                        m_fMinIntensity = plotData[rtRow].m_PD[mzSample].Intensity;

                    if (plotData[rtRow].m_PD[mzSample].MZ > m_fMaxMZ)
                        m_fMaxMZ = plotData[rtRow].m_PD[mzSample].MZ;
                    else if (plotData[rtRow].m_PD[mzSample].MZ < m_fMinMZ)
                        m_fMinMZ = plotData[rtRow].m_PD[mzSample].MZ;
                }
            }


            
        }

        public InternalPlotData GetDataSubset()
        {

            try
            {
                List<DataPoint> currentRow = new List<DataPoint>();
                List<Spectrum> readyRows = new List<Spectrum>();
                List<DataTriplet> helperList = new List<DataTriplet>();

                float fIntensityTreshold = m_PlotSettings.PlotSubsetDescription.m_fIntensityTreshold;
                if (m_PlotSettings.PlotType != PlotType.Points)
                    fIntensityTreshold = 100;

                // size of all samples
                int iGlobalSize = 0;

                for (int row = 0; row < RetentionScansCount; row++)
                {
                    iGlobalSize += m_PlotSamples[row].NumberOfSamples;
                }
               
                iGlobalSize = (int)(iGlobalSize * fIntensityTreshold / 100.0f);
                // filling data
                for (int row = 0; row < RetentionScansCount; row++)
                {
                    // checking if rt fits in settings
                    if (m_PlotSamples[row].m_RT < m_PlotSettings.PlotSubsetDescription.m_fMinRetentionTime ||
                        m_PlotSamples[row].m_RT > m_PlotSettings.PlotSubsetDescription.m_fMaxRetentionTime)
                        continue;
                    // clear previous row
                    currentRow.Clear();

                    // read all samples
                    for (int sample = 0; sample < m_PlotSamples[row].NumberOfSamples; sample++)
                    {
                        // checking if mz fits in settings
                        if (m_PlotSamples[row].m_PD[sample].MZ < m_PlotSettings.PlotSubsetDescription.m_fMinMZ ||
                            m_PlotSamples[row].m_PD[sample].MZ > m_PlotSettings.PlotSubsetDescription.m_fMaxMZ)
                            continue;

                        // peak filtering works only for PointsPlot
                        if (m_PlotSettings.PlotType == PlotType.Points)
                        {
                            if (m_PlotSettings.PlotSubsetDescription.m_bGlobalIntensity == false)
                            {
                                currentRow.Add(m_PlotSamples[row].m_PD[sample]);
                            }
                            else
                            {
                                DataTriplet tmp = new DataTriplet();
                                tmp.m_fIntensity = m_PlotSamples[row].m_PD[sample].Intensity;
                                tmp.m_fMZ = m_PlotSamples[row].m_PD[sample].MZ;
                                tmp.m_fRT = m_PlotSamples[row].m_RT;
                                helperList.Add(tmp);
                            }
                        }
                        else
                        {
                            currentRow.Add(m_PlotSamples[row].m_PD[sample]);
                        }
                    }

                    // get peak filtering in local filtering
                    if (m_PlotSettings.PlotSubsetDescription.m_bGlobalIntensity == false && m_PlotSettings.PlotType == PlotType.Points)
                    {
                        currentRow.Sort(CompareDataSampleByIntensity);
                        currentRow.Reverse();
                        int size = (int)(currentRow.Count * fIntensityTreshold / 100.0f);
                        currentRow.RemoveRange(size, currentRow.Count - size);
                        currentRow.Sort(CompareDataSampleByMz);
                        readyRows.Add(new Spectrum(currentRow.ToArray(), m_PlotSamples[row].m_RT));
                    }
                    else
                    {
                        readyRows.Add(new Spectrum(currentRow.ToArray(), m_PlotSamples[row].m_RT));
                    }

                }
                // global intensity filter
                if (m_PlotSettings.PlotSubsetDescription.m_bGlobalIntensity && m_PlotSettings.PlotType == PlotType.Points)
                {
                    readyRows.Clear();
                    if (helperList.Count > iGlobalSize)
                    {
                        helperList.Sort(CompareTripletByIntensityDesc);
                        helperList.RemoveRange(iGlobalSize - 1, helperList.Count - iGlobalSize);
                    }
                    if (helperList.Count > 0)
                    {
                        helperList.Sort(CompareTripletByRT);
                        //List<DataSample> currentRow = new List<DataSample>();
                        float fCurrentRT = helperList[0].m_fRT;
                        for (int i = 0; i < helperList.Count; i++)
                        {
                            if (fCurrentRT != helperList[i].m_fRT)
                            {
                                currentRow.Sort(CompareDataSampleByMz);
                                readyRows.Add(new Spectrum(currentRow.ToArray(), fCurrentRT));
                                fCurrentRT = helperList[i].m_fRT;
                                currentRow.Clear();
                            }
                            DataPoint sCurrentSample = new DataPoint();
                            sCurrentSample.Intensity = helperList[i].m_fIntensity;
                            sCurrentSample.MZ = helperList[i].m_fMZ;
                            currentRow.Add(sCurrentSample);
                        }
                        currentRow.Sort(CompareDataSampleByMz);
                        readyRows.Add(new Spectrum(currentRow.ToArray(), helperList[helperList.Count - 1].m_fRT));
                    }
                }
                // logarithmic scale
                if (m_PlotSettings.LogarithmicIntensity)
                {
                    for (int i = 0; i < readyRows.Count; i++)
                    {
                        for (int j = 0; j < readyRows[i].m_PD.Length; j++)
                        {
                            readyRows[i].m_PD[j].Intensity = (float)Math.Log10((Double)readyRows[i].m_PD[j].Intensity + 1.0);
                        }
                    }
                }
                if (!m_PlotSettings.DisableResampling && m_PlotSettings.PlotType != PlotType.Surface)
                {
                    List<Spectrum> readyRowsTmp = new List<Spectrum>();
                    List<DataPoint> currentRowTmp = new List<DataPoint>();
                    ///////////////////////////////////////////////////////////////////////////
                    // RT RESOLUTION                
                    float RtStep = (float)(readyRows.Count) / (float)m_PlotSettings.PlotSubsetDescription.m_iResolutionRetentionTime;
                    if (RtStep < 1)
                    {
                        RtStep = 1;
                    }
                    int CurrentRtIndex = 0;
                    float fToWhatRt = RtStep;
                    readyRowsTmp.Clear();
                    //iCurrentRtIndex = 0;
                    //Spectrum currentSpec;
                    //readyRows.Clear();

                    // until we have checked all rt scans
                    while (CurrentRtIndex < readyRows.Count)
                    {                        
                        // for all rt scans in current range search for max intensity
                        currentRowTmp.Clear();
                        for (int i = CurrentRtIndex; i < Math.Ceiling(fToWhatRt) && i < readyRows.Count; i++)
                        {
                            for (int j = 0; j < readyRows[i].NumberOfSamples; j++)
                            {
                                currentRowTmp.Add(readyRows[i].m_PD[j]);
                            }
                        }
                        currentRowTmp.Sort(CompareDataSampleByMz);
                        // add row
                        readyRowsTmp.Add(new Spectrum(currentRowTmp.ToArray(), readyRows[CurrentRtIndex].m_RT));
                        // update range
                        CurrentRtIndex = (int)Math.Ceiling(fToWhatRt);
                        fToWhatRt += RtStep;                        
                    }
                    //////////////////////////////////////////////////////////////////////////
                    // MZ - RESOLUTION             
                    float fMzStep = (m_PlotSettings.PlotSubsetDescription.m_fMaxMZ - m_PlotSettings.PlotSubsetDescription.m_fMinMZ) / (m_PlotSettings.PlotSubsetDescription.m_iResolutionMZ - 1);

                    int iCurrentRtIndex = 0;
                    readyRows.Clear();

                    while (iCurrentRtIndex < readyRowsTmp.Count)
                    {
                        int iCurrentMzIndex = 0;
                        int iCurrentRowVertex = 0;
                        float fCurrentOffset = readyRowsTmp[iCurrentRtIndex].m_PD[0].MZ;

                        Spectrum rowData = readyRowsTmp[iCurrentRtIndex];
                        DataPoint curPoint = rowData.m_PD[0];
                        currentRowTmp.Clear();
                        for (int i = 0; i < rowData.NumberOfSamples; i++)
                        {
                            if (rowData.m_PD[i].MZ > fCurrentOffset)
                            {
                                if (currentRowTmp.Count == 0)
                                {
                                    currentRowTmp.Add(curPoint);
                                }
                                else if (currentRowTmp[currentRowTmp.Count - 1].MZ != curPoint.MZ)
                                {
                                    currentRowTmp.Add(curPoint);
                                }
                                curPoint = rowData.m_PD[i];
                                while (fCurrentOffset < curPoint.MZ)
                                {
                                    fCurrentOffset += fMzStep;
                                }
                            }
                            if (rowData.m_PD[i].Intensity > curPoint.Intensity)
                            {
                                curPoint = rowData.m_PD[i];
                            }
                        }

                        readyRows.Add(new Spectrum(currentRowTmp.ToArray(), readyRowsTmp[iCurrentRtIndex].m_RT));
                        iCurrentRtIndex++;

                    }
                }

                InternalPlotData subsetData = new InternalPlotData(readyRows.ToArray());
                subsetData.PlotSettings = (InternalPlotSettings)m_PlotSettings.Clone();

                //int[] lotsOfData = new int[500000000];
                return subsetData;
            }
            catch (OutOfMemoryException)
            {
                InternalPlotData subsetData = new InternalPlotData(new Spectrum[0]);
                subsetData.PlotSettings = (InternalPlotSettings)m_PlotSettings.Clone();
                return subsetData;
            }
            catch
            {
                MessageBox.Show("Error in GetDataSubset");
                InternalPlotData subsetData = new InternalPlotData(new Spectrum[0]);
                subsetData.PlotSettings = (InternalPlotSettings)m_PlotSettings.Clone();
                return subsetData;
            }

        }

        /// <summary>
        /// Helper method for sorting triplet by Intensity in descending order (no reverse required)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static int CompareTripletByIntensityDesc(DataTriplet x, DataTriplet y)
        {
            if (x.m_fIntensity > y.m_fIntensity)
            {
                return -1;
            }
            else if (x.m_fIntensity == y.m_fIntensity)
            {
                return 0;
            }
            else
                return 1;
        }

        /// <summary>
        /// Helper method for sorting Triplets by RT
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static int CompareTripletByRT(DataTriplet x, DataTriplet y)
        {
            if (x.m_fRT > y.m_fRT)
            {
                return 1;
            }
            else if (x.m_fRT == y.m_fRT)
            {
                return 0;
            }
            else
                return -1;
        }

        /// <summary>
        /// Helper method for sorting Intensity
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static int CompareDataSampleByIntensity(DataPoint x, DataPoint y)
        {
            if (x.Intensity > y.Intensity)
                return 1;
            else if (x.Intensity == y.Intensity)
                return 0;
            else
                return -1;
        }

        /// <summary>
        /// Helper method for sorting data by MZ
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static int CompareDataSampleByMz(DataPoint x, DataPoint y)
        {
            if (x.MZ > y.MZ)
                return 1;
            else if (x.MZ == y.MZ)
                return 0;
            else
                return -1;
        }

    }

    //public class sGradientControl:ICloneable
    //{
    //    public Color m_cMinColor= Color.Yellow;
    //    public Color m_cMaxColor= Color.Blue;
    //    public float m_fFactor= 1.0f;//5.0f;

    //    /// <summary>
    //    /// Computes color of the vertex
    //    /// </summary>
    //    /// <param name="fScale">Scale between [0,1] 0- min value, 1-max value</param>
    //    /// <returns></returns>
    //    public Color GetColor(float fScale)
    //    {
    //        int R = (int)(Math.Pow((float)fScale, (float)m_fFactor) * m_cMaxColor.R + (1-Math.Pow((float)(fScale), (float)m_fFactor)) * m_cMinColor.R);
    //        int G = (int)(Math.Pow((float)fScale, (float)m_fFactor) * m_cMaxColor.G + (1 - Math.Pow((float)(fScale), (float)m_fFactor)) * m_cMinColor.G);
    //        int B = (int)(Math.Pow((float)fScale, (float)m_fFactor) * m_cMaxColor.B + (1-Math.Pow((float)(fScale), (float)m_fFactor)) * m_cMinColor.B);
    //        return Color.FromArgb(R, G, B);
    //    }


    //    #region ICloneable Members

    //    public object Clone()
    //    {
    //        return MemberwiseClone();
    //    }

    //    #endregion
    //}



    /// <summary>
    /// Helper class for drawing line plot - manages a color chain
    /// </summary>
    class ColorChanger
    {
        Color[] m_aColors = new Color[7] { Color.Green, Color.Blue, Color.Red, Color.Aqua, Color.Purple, Color.Yellow, Color.Black };
        int m_iCurColor = 0;
        public Color Next()
        {
            m_iCurColor = (m_iCurColor + 1) % m_aColors.Length;
            return m_aColors[m_iCurColor];
        }

        public Color GetColor()
        {
            return System.Drawing.Color.FromArgb(50, m_aColors[m_iCurColor]);// m_aColors[m_iCurColor];
        }
    }
}
